global with sharing class Callidus_DataSourceBatchUtil implements Database.Batchable<sObject>
{
    public String query1;
    public String query2;
    public String query3;
    global Map<ID, Map<String, String>> additionalUserFieldsMap = new Map<ID, Map<String, String>>();
    global Map<String, String> tempMap = new Map<String, String>();
    global List<String> additionalUserFieldsList;
    
    
    global Callidus_DataSourceBatchUtil()
    {
    	additionalUserFieldsList = new String[]{'Value1__c', 'Value2__c', 'Value3__c', 'Value4__c', 'Value5__c', 'Value6__c', 'Value7__c', 'Value8__c', 'Value9__c', 'Value10__c', 'Value11__c', 'Value12__c'};
    	tempMap.clear();
        query1 = getUserQueryForDataSource();
        query2 = getQueryForDataSourceCreation();
        query3 = getQueryForPlanAccess();
    }
    
    global String getUserQueryForDataSource()
    {
        String query = '';

        DataAccessControlUtil dacu = new DataAccessControlUtil();   

        SObject bdObj = new BatchDetail__c();
        Map<String, Schema.SObjectField> bdFieldMap = dacu.getFieldMap(bdObj);
        
        String[] bdExpectedFields = new String[]{'Field_Name__c'};
        Set<String> allowedFields = dacu.getAccessibleFields(bdObj, bdFieldMap, bdExpectedFields);
        BatchDetail__c[] bdList = new BatchDetail__c[]{};

        if(!allowedFields.isEmpty())
        {
            query = 'Select ';
            for(String roField: allowedFields)
            {
                query += roField+', ';
            }
            query = query.substring(0, query.length() - 2);
            query += ' from BatchDetail__c ';

	        bdList = Database.query(query);
	        if(bdList.size() == 0)
	        	additionalUserFieldsList.clear();
        }
            
        SObject uObj = new User();
        Map<String, Schema.SObjectField> uFieldMap = dacu.getFieldMap(uObj);
        
        String[] userExpectedFields = new String[]{'Id','LastName','FirstName','IsActive', 'Title'};
        BatchDetail__c bd;
        for(Integer i=0; i< bdList.size(); i++)
        {
        	bd = bdList[i];
        	userExpectedFields.add(bd.Field_Name__c);
        	tempMap.put(additionalUserFieldsList.get(i), bd.Field_Name__c);
        }
         
        allowedFields = dacu.getAccessibleFields(uObj, uFieldMap, userExpectedFields);

        if(!allowedFields.isEmpty())
        {
            query = 'Select ';
            for(String roField: allowedFields)
            {
                query += roField+', ';
            }
            query = query.substring(0, query.length() - 2);
            query += ' , UserRole.Id, UserRole.Name from User u where u.FirstName != :license ';
        }
        return query;    
    }
    
    global String getQueryForDataSourceCreation()
    {
        String cdsQuery = '';

        DataAccessControlUtil dacu = new DataAccessControlUtil();   

        SObject cdsObj = new CallidusDataSource__c();
        
        Map<String, Schema.SObjectField> fieldMap = dacu.getFieldMap(cdsObj);
        
        //Note: Whenever a new Custom field has to be updated, add it to the below Set.
        //Reason: The size of the below set will be compared with the size of the filtered set 
        Set<String> cdsUpdatedFields = new Set<String>{'Name','LastName__c','FirstName__c','UserID__c','UserRoleId__c','UserRoleName__c','User_Title__c', 'IsActive__c'}; 
        Set<String> filteredFields = dacu.getFilteredUpdateableFields(fieldMap, cdsUpdatedFields);

        //Note: Whenever a new Custom field has to be created, add it to the below Set.
        //Reason: The size of the below set will be compared with the size of the filtered set 
        Set<String> cdsCreateableFields = new Set<String>{'Name','LastName__c','FirstName__c','UserID__c','UserRoleId__c','UserRoleName__c','User_Title__c', 'IsActive__c'}; 
        Set<String> filteredCreateableFields = dacu.getFilteredCreateableFields(fieldMap, cdsCreateableFields);

        //Enters only if the filteredList size is equivalent to the size of expected createable and updateable fields List 
        if(filteredFields.size() == cdsUpdatedFields.size() && filteredCreateableFields.size() == cdsCreateableFields.size())
        {
            cdsQuery = 'Select ';
            for(String roField: filteredFields)
            {
                cdsQuery += roField+', ';
            }
            cdsQuery = cdsQuery.substring(0, cdsQuery.length() - 2);
            cdsQuery += ' from CallidusDataSource__c ';
        }
        return cdsQuery;     
    }

    global String getQueryForPlanAccess()
    {
        String cpacQuery = '';

        DataAccessControlUtil dacu = new DataAccessControlUtil();   

        SObject cpacObj = new Callidus_Plan_Access_Control__c();
        
        Map<String, Schema.SObjectField> fieldMap = dacu.getFieldMap(cpacObj);
        
        //Note: Whenever a new Custom field has to be updated, add it to the below Set.
        //Reason: The size of the below set will be compared with the size of the filtered set 
        Set<String> cpacUpdatedFields = new Set<String>{'Participant__c','Is_Access_Allowed__c','Name'}; 
        Set<String> filteredFields = dacu.getFilteredUpdateableFields(fieldMap, cpacUpdatedFields);

        //Note: Whenever a new Custom field has to be created, add it to the below Set.
        //Reason: The size of the below set will be compared with the size of the filtered set 
        Set<String> cpacCreateableFields = new Set<String>{'Participant__c','Is_Access_Allowed__c','Name'}; 
        Set<String> filteredCreateableFields = dacu.getFilteredCreateableFields(fieldMap, cpacCreateableFields);

        //Enters only if the filteredList size is equivalent to the size of expected createable and updateable fields List 
        if(filteredFields.size() == cpacUpdatedFields.size() && filteredCreateableFields.size() == cpacCreateableFields.size())
        {
            cpacQuery = 'Select ';
            for(String roField: filteredFields)
            {
                cpacQuery += roField+', ';
            }
            cpacQuery = cpacQuery.substring(0, cpacQuery.length() - 2);
            cpacQuery += ' from Callidus_Plan_Access_Control__c ';
        }
        return cpacQuery;    
    }

    global database.querylocator start(Database.BatchableContext BC)
    {
        String license = 'License';
        return Database.getQueryLocator(query1);
    }
    
    global void execute(Database.BatchableContext BC, List<sObject> scope)
    {
        List<ID> userIdList = new List<ID>();
        CallidusDataSource__c[] newDataSource = new List<CallidusDataSource__c>();
        CallidusDataSource__c[] existingDataSource = new List<CallidusDataSource__c>();
        CallidusDataSource__c cUser = null;
        Map<ID, CallidusDataSource__c> cdsMap =  new Map<ID, CallidusDataSource__c>();
        additionalUserFieldsMap.clear();
        Map<String, String> valueMap = new Map<String, String>(); 
        for(SObject s: scope)
        {
            User u = (User)s;
            userIdList.add(u.id);
            
            if(!tempMap.isEmpty())
        		valueMap = new Map<String, String>();
        		
            for(String str: tempMap.keySet())
            {
	            valueMap.put(str, String.valueOf(s.get(tempMap.get(str))));
            	additionalUserFieldsMap.put(u.id, valueMap);
            }
        }
		
        for(CallidusDataSource__c[] cdsList : Database.query(query2+' where UserID__c in : userIdList'))
        {
            for(CallidusDataSource__c cds : cdsList)
            {
                cdsMap.put(cds.UserID__c, cds);
            }
        }
         
        Callidus_Plan_Access_Control__c[] newCPACList = new List<Callidus_Plan_Access_Control__c>();
        Map<ID, Callidus_Plan_Access_Control__c> cpacMap =  new Map<ID, Callidus_Plan_Access_Control__c>();
        Callidus_Plan_Access_Control__c cpacUser = null; 

        for(Callidus_Plan_Access_Control__c[] cpacList : Database.query(query3+' where Participant__c in : userIdList'))
        {
            for(Callidus_Plan_Access_Control__c cpac : cpacList)
            {
                cpacMap.put(cpac.Participant__c, cpac);
            }
        }
		SObject sobj;
        for(SObject s: scope)
        {
            User user = (User)s;
            
            cUser = null;
            cpacUser = null;
            if(!cdsMap.isEmpty())
                cUser = cdsMap.get(user.Id);
            
            if(!cpacMap.isEmpty())
                cpacUser = cpacMap.get(user.Id); 
            
            valueMap = additionalUserFieldsMap.get(user.id);
              
            if(cUser != null)
            {
                if(user.FirstName != null)
                {
                    cUser.Name = user.FirstName+' '+user.LastName;
                }
                else if(user.FirstName == null)
                {
                    cUser.Name = user.LastName;
                }
                
                cUser.FirstName__c = user.FirstName;
                cUser.LastName__c = user.LastName;
                cUser.UserID__c = user.Id;
                cUser.UserRoleId__c = user.UserRole.Id;
                cUser.UserRoleName__c = user.UserRole.Name;
                cUser.User_Title__c = user.Title;
                cUser.IsActive__c = user.IsActive;
                
                if(!tempMap.isEmpty())
                	cleanValueFields(cUser);

                sobj = cUser;
                if(valueMap != null && valueMap.size() > 0)
                	cUser = (CallidusDataSource__c)getAdditionalUserFieldsAssigned(sObj, valueMap);
                
                existingDataSource.add(cUser);
                if(existingDataSource.size() == 200)
                {
                    update existingDataSource;
                    existingDataSource.clear();
                }
            }
            else
            {
                cUser = new CallidusDataSource__c();
    
                if(user.FirstName != null)
                {
                    cUser.Name = user.FirstName+' '+user.LastName;
                }
                else if(user.FirstName == null)
                {
                    cUser.Name = user.LastName;
                }
    
                cUser.FirstName__c = user.FirstName;
                cUser.LastName__c = user.LastName;
                cUser.UserID__c = user.Id;
                cUser.UserRoleId__c = user.UserRole.Id;
                cUser.UserRoleName__c = user.UserRole.Name;
                cUser.User_Title__c = user.Title;
                cUser.IsActive__c = user.IsActive;

                if(!tempMap.isEmpty())
                	cleanValueFields(cUser);

                sobj = cUser;
                if(valueMap != null && valueMap.size() > 0)
                	cUser = (CallidusDataSource__c)getAdditionalUserFieldsAssigned(sObj, valueMap);
                
                newDataSource.add(cUser);
                if(newDataSource.size() == 200)
                {
                    insert newDataSource;
                    newDataSource.clear();
                }
            }
            
            if(cpacUser == null)
            {
                cpacUser = new Callidus_Plan_Access_Control__c();
                cpacuser.participant__c = user.Id;
                cpacUser.name = user.FirstName+' '+user.LastName;
                newCPACList.add(cpacuser);
            }
        }//end of for
        
        if(newDataSource.size() > 0)
        {
            insert newDataSource;
            newDataSource.clear();
        }
        if(existingDataSource.size() > 0)
        {
            update existingDataSource;
            existingDataSource.clear();
        }

        if(newCPACList.size() > 0)
        {
            insert newCPACList;
            newCPACList.clear();
        }
    }//end of execute()
    
    private CallidusDataSource__c cleanValueFields(CallidusDataSource__c cUser)
    {
    	cuser.Value1__c = '';
    	cuser.Value2__c = '';
    	cuser.Value3__c = '';
    	cuser.Value4__c = '';
    	cuser.Value5__c = '';
    	cuser.Value6__c = '';
    	cuser.Value7__c = '';
    	cuser.Value8__c = '';
    	cuser.Value9__c = '';
    	cuser.Value10__c = '';
    	cuser.Value11__c = '';
    	cuser.Value12__c = '';
    	cuser.Value13__c = '';
    	cuser.Value14__c = '';
    	cuser.Value15__c = '';
    	cuser.Value16__c = '';
    	cuser.Value17__c = '';
    	cuser.Value18__c = '';
    	cuser.Value19__c = '';
    	cuser.Value20__c = '';
    	cuser.Value21__c = '';
    	cuser.Value22__c = '';
    	cuser.Value23__c = '';
    	cuser.Value24__c = '';
    	cuser.Value25__c = '';
    	cuser.Value26__c = '';
    	cuser.Value27__c = '';
    	cuser.Value28__c = '';
    	cuser.Value29__c = '';
    	cuser.Value30__c = '';
    	return cUser;
    }
    
    private SObject getAdditionalUserFieldsAssigned(Sobject sobj, Map<String, String> valueMap)
    {
    	for(String s: valueMap.keyset())
    	{
    		sobj.put(s, valueMap.get(s));
    	}
    	return sobj;
    }
    
    global void finish(Database.BatchableContext BC)
    {
        Callidus_CommonUtil ccu = new Callidus_CommonUtil();
        
        EmailTemplate emailTemplate = [select Id,Name,Subject,body from EmailTemplate where name like :'Callidus_Synchronize_User_Data_Mail'];
                                                  
        Messaging.SingleEmailMessage[] mailList = new Messaging.SingleEmailMessage[]{};
                            
        mailList.add(ccu.createNewEmail(null, UserInfo.getUserId(), emailTemplate.Id, null, false, false));
        
        Messaging.sendEmail(mailList);      
        
        List<Task> tList = new List<Task>();
        Task t = ccu.newTask(null, UserInfo.getUserId(), null, 'Synchronizing user data completed successfully.', 'Synchronizing user data completed successfully.', Date.today(), DateTime.now(), true, 'High', 'Finished', 'Priority');
        tList.add(t);
        String message = ccu.insertTasks(tList);
    }
    
    public static testMethod void testBatch() 
    {
        User[] uList = new User[]{};
        for(integer i = 0; i<200; i++)
        {
            User u = new User(Lastname = 'testBatchApexUser', Username = 'enduser'+i+'@gmail.com', Email = 'enduser'+i+'@gmail.com', Alias = 'enduser', CommunityNickname = 'euser'+i, TimeZoneSidKey = 'America/Los_Angeles', LocaleSidKey = 'en_US', EmailEncodingKey = 'ISO-8859-1', ProfileId = UserInfo.getProfileId(), LanguageLocaleKey = 'en_US', ManagerId = UserInfo.getUserId());
            uList.add(u);
        }
        insert uList;
        
        Test.StartTest();
        Callidus_DataSourceBatchUtil cdsbu = new Callidus_DataSourceBatchUtil();
        cdsbu.query1 += ' Limit 200 ';
        ID batchprocessid = Database.executeBatch(cdsbu);
        
        cdsbu = new Callidus_DataSourceBatchUtil();
        cdsbu.query1 += ' Limit 200 ';
        batchprocessid = Database.executeBatch(cdsbu);
        Test.StopTest();
        
        String endUser = 'testBatchApexUser';
        System.AssertEquals(database.countquery('SELECT count()'+' FROM User WHERE Lastname = :endUser'), 200);
    }

    public static testMethod void testBatch2() 
    {
        User[] uList = new User[]{};
        for(integer i = 0; i<20; i++)
        {
            User u = new User(Firstname = 'Test', Lastname = 'testBatchApexUser', Username = 'enduser'+i+'@gmail.com', Email = 'enduser'+i+'@gmail.com', Alias = 'enduser', CommunityNickname = 'euser'+i, TimeZoneSidKey = 'America/Los_Angeles', LocaleSidKey = 'en_US', EmailEncodingKey = 'ISO-8859-1', ProfileId = UserInfo.getProfileId(), LanguageLocaleKey = 'en_US', ManagerId = UserInfo.getUserId());
            uList.add(u);
        }
        insert uList;
        
        Test.StartTest();
        Callidus_DataSourceBatchUtil cdsbu = new Callidus_DataSourceBatchUtil();
        cdsbu.query1 += ' Limit 20 ';
        ID batchprocessid = Database.executeBatch(cdsbu);
        
        cdsbu = new Callidus_DataSourceBatchUtil();
        cdsbu.query1 += ' Limit 20 ';
        batchprocessid = Database.executeBatch(cdsbu);
        Test.StopTest();
        
        String endUser = 'testBatchApexUser';
        System.AssertEquals(database.countquery('SELECT count()'+' FROM User WHERE Lastname = :endUser'), 20);
    }
    
    static testMethod void dacuTestCase() 
    {
        DataAccessControlUtil dacu = new DataAccessControlUtil();
        SObject sObj = new CallidusDataSOurce__c();
        Map<String, Schema.Sobjectfield> fieldsMap = dacu.getFieldMap(sObj);
        String[] expectedFieldMap = new String[]{'Id','Name'};
        Set<String> allowedFields = dacu.getAccessibleFields(sObj, fieldsMap, expectedFieldMap);
        Set<String> filteredList = dacu.getFilteredCreateableFields(fieldsMap, allowedFields);
        filteredList = dacu.getFilteredUpdateableFields(fieldsMap, allowedFields);
        system.assertEquals('success', dacu.isCreateableObjectByUser(sObj));
        system.assertEquals('success', dacu.isUpdateableObjectByUser(sObj));
        system.assertEquals('success', dacu.isDeleteableObjectByUser(sObj));
        dacu.pageMessage(sobj, 'create');
        
    }
      
    
}